package com.leyou.item.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.leyou.common.constant.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.entity.Category;
import com.leyou.item.entity.Sku;
import com.leyou.item.entity.Spu;
import com.leyou.item.entity.SpuDetail;
import com.leyou.item.mapper.SkuMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 只要两个对象都在IOC容器中，就可以互相注入。
 * 但是这里处理器除外，处理器中可以注入service，但是service中不能注入处理器。
 * spring中事务的传播行为，指的是两个service互相调用的时候，事务的传播特性。
 * 事务的传播行为默认值为propagation = Propagation.REQUIRED
 * 表示当前业务方法如果有事务，被调用的业务方法就加入到当前事务中，
 * 如果没有，则开启一个新事务。
 */
@Service
@Transactional
public class ItemService {

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private SpuDetailMapper spuDetailMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private BrandService brandService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    public PageResult<SpuDTO> spuPageQuery(Integer page, Integer rows, String key, Boolean saleable) {
        //设置分页信息
        PageHelper.startPage(page, rows);
        //创建一个封装复杂条件查询的对象，凡是和数据库交互都要用entity
        Example example = new Example(Spu.class);
        //封装条件
        Example.Criteria criteria = example.createCriteria();
        if(!StringUtils.isEmpty(key)){
            criteria.andLike("name", "%"+key+"%");
        }
        if(saleable!=null){
            //mysql数据库中使用tinyint来作为布尔值，java传true到数据库自动保存为1，传false自动保存为0
            //如果数据库中的tinyint值是非0，查询封装到java中就是true，如果是0则为false。
            criteria.andEqualTo("saleable", saleable);
        }
        //分页查询
        List<Spu> spus = spuMapper.selectByExample(example);
        //判空
        if(CollectionUtils.isEmpty(spus)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //得到pageHelper的分页对象
        PageInfo<Spu> pageInfo = new PageInfo<>(spus);
        //获取到Spu集合
        List<Spu> list = pageInfo.getList();
        //将spu集合转为spuDTO集合
        List<SpuDTO> spuDTOS = BeanHelper.copyWithCollection(list, SpuDTO.class);
        //给SpuDTO集合中每个SpuDTO对象的categoryName和brandName赋值
        handlerCategoryNameAndBrandName(spuDTOS);
        //转成自定义的分页对象
        PageResult<SpuDTO> pageResult = new PageResult<>(
                pageInfo.getTotal(),
                pageInfo.getPages(),
                spuDTOS
        );
        return pageResult;
    }

    //给SpuDTO集合中每个SpuDTO对象的categoryName和brandName赋值
    private void handlerCategoryNameAndBrandName(List<SpuDTO> spuDTOS) {
        spuDTOS.forEach(spuDTO -> {
            String categoryNames = categoryService.findCategoriesByIds(spuDTO.getCategoryIds())//得到三个分类对象的集合
                    .stream()//将分类集合转流
                    .map(Category::getName)//收集到每个Category对象中的name值
                    .collect(Collectors.joining("|"));//将收集到的所有name值以|拼接成一个字符串
            //将分类名称赋值给SpuDTO对象
            spuDTO.setCategoryName(categoryNames);
            //获取品牌名称
            String brandName = brandService.findBrandById(spuDTO.getBrandId()).getName();
            //将品牌名称赋值给SpuDTO对象
            spuDTO.setBrandName(brandName);
        });
    }

    public void saveItem(SpuDTO spuDTO) {
        try {
            //将SpuDTO对象转成Spu
            Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
            //设置新添加的商品为下架状态
            spu.setSaleable(false);
            //保存Spu
            spuMapper.insertSelective(spu);

            //获取spuDetail对象
            SpuDetail spuDetail = spuDTO.getSpuDetail();
            //给主键赋值
            spuDetail.setSpuId(spu.getId());
            //保存SpuDetail
            spuDetailMapper.insertSelective(spuDetail);

            //获取Sku集合
            List<Sku> skus = spuDTO.getSkus();
            //给sku集合中所有Sku对象的SpuId赋值
            skus.forEach(sku -> {
                sku.setSpuId(spu.getId());
                sku.setCreateTime(new Date());
                sku.setUpdateTime(new Date());
            });
            skuMapper.insertList(skus);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }

    /**
     * 商品上下架
     * @param id
     * @param saleable
     */
    public void updateSaleable(Long id, Boolean saleable) {
        try {
            Spu record = new Spu();
            record.setId(id);
            record.setSaleable(saleable);
            spuMapper.updateByPrimaryKeySelective(record);

            //方式一：上下架时，通过feign接口同步去添加或删除索引库和添加或删除静态页
            //方式二：上下架时，将上下架的商品放入MQ中，直接返回结果【选择的异步方式】

            //指定routingKey
            String routingKey = saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY;
            //向mq发送消息
            amqpTemplate.convertAndSend(MQConstants.Exchange.ITEM_EXCHANGE_NAME, routingKey, id);
        }catch (Exception e){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }

    public List<Sku> findSkusBySpuId(Long id) {
        Sku record = new Sku();
        record.setSpuId(id);
        List<Sku> skus = skuMapper.select(record);
        if(CollectionUtils.isEmpty(skus)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skus;
    }

    public SpuDetail findSpuDetailBySpuId(Long id) {
        SpuDetail spuDetail = spuDetailMapper.selectByPrimaryKey(id);
        if(spuDetail==null){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }

    public SpuDTO findSpuDTObySpuId(Long id) {
        //根据id查询Spu对象
        Spu spu = spuMapper.selectByPrimaryKey(id);
        //判空
        if(spu==null){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        //将Spu对象转成SpuDTO对象
        SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
        //根据spuId查询spu集合
        List<Sku> skus = findSkusBySpuId(id);
        //将skus赋值给SpuDTO的属性
        spuDTO.setSkus(skus);
        //根据spuId查询SpuDetail对象
        SpuDetail spuDetail = findSpuDetailBySpuId(id);
        //将spuDetail对象封装到SpuDTO对象中
        spuDTO.setSpuDetail(spuDetail);
        return spuDTO;
    }

    public List<Sku> findSkusByIds(List<Long> ids) {
        //批量查询sku
        List<Sku> list = skuMapper.selectByIdList(ids);
        if(CollectionUtils.isEmpty(list)){
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return list;
    }

    public void minusStock(Map<Long, Integer> paramMap) {
        try {
            //entry 键值对
            paramMap.entrySet().forEach(entry->{
                //key
                Long skuId = entry.getKey();
                //value
                Integer num = entry.getValue();
                //根据skuId查询sku对象
                Sku sku = skuMapper.selectByPrimaryKey(skuId);
                //封装条件
                Sku record = new Sku();
                record.setId(skuId);
                record.setStock(sku.getStock()-num);
                skuMapper.updateByPrimaryKeySelective(record);
            });
        }catch (Exception e){
            throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH_ERROR);
        }
    }

}
